#!/usr/bin/python

import optparse, math, Image

def get_pixel(img, (x, y)):
    p = (x % img.size[0], y % img.size[0])
    return img.getpixel(p)

def get_dxdy(img, (x, y)):
    a = get_pixel(img, (x, y))
    b = get_pixel(img, (x + 1, y))
    c = get_pixel(img, (x, y + 1))
    dx = b - a
    dy = c - a
    return (dx / 250.0, dy / 250.0)

def normalize((x, y, z)):
    le = math.sqrt(x * x + y * y + z * z)
    return (x / le, y / le, z / le)

def calc_normal(img, (x, y), scale):
    (dx, dy) = get_dxdy(img, (x, y))
    nx = -dx * scale
    ny = -dy * scale
    nz = 1.0
    return normalize((nx, ny, nz))

def encode_normal((nx, ny, nz)):
    r = nx * 127.5 + 127.5
    g = ny * 127.5 + 127.5
    b = nz * 127.5 + 127.5
    return (int(r), int(g), int(b))

def dot3(ifile, ofile, scale):
    inp = Image.open(ifile).convert('L')
    out = Image.new('RGB', inp.size)
    (sx, sy) = inp.size
    for y in range(sy):
        # print "Processing line", y, "of", sy-1
        for x in range(sx):
            p = (x, y)
            out.putpixel(p, encode_normal(calc_normal(inp, p, scale)))
    out.save(ofile)

p = optparse.OptionParser(usage="usage: %prog [options] SOURCE DEST")
p.add_option('-s', '--scale', dest='scale', type='float', help='bump map scale factor', default=1.0)
(opts, args) = p.parse_args()

if len(args) != 2:
    p.error("incorrect number of arguments")

dot3(args[0], args[1], opts.scale)
